﻿package away3d.entities ;


import away3d.animators.data.AnimationBase ;
import away3d.animators.data.AnimationStateBase ;
import away3d.animators.data.NullAnimation ;
import away3d.bounds.BoundingSphere ;
import away3d.bounds.BoundingVolumeBase ;
import away3d.containers.View3D ;
import away3d.core.base.IRenderable ;
import away3d.core.partition.EntityNode ;
import away3d.core.partition.RenderableNode ;
import away3d.materials.MaterialBase ;
import away3d.materials.SegmentMaterial ;
import away3d.primitives.data.Segment ;
import flash.display3D.Context3D ;
import flash.display3D.IndexBuffer3D ;
import flash.display3D.VertexBuffer3D ;
import flash.geom.Vector3D ;



class SegmentSet extends Entity implements IRenderable
{
    
    
    private var _material           : MaterialBase ;
    private var _nullAnimation      : NullAnimation ;
    private var _animationState     : AnimationStateBase ;
    private var _vertices           : Vector<Float> ;
    private  var _segments          : Vector<Segment> ;
    private var _numVertices        : Int  ;
    private var _indices            : Vector<Int> ;
    private var _numIndices         : Int  ;
    private var _vertexBufferDirty  : Bool ;
    private var _indexBufferDirty   : Bool ;
    private var _vertexBuffer       : VertexBuffer3D ;
    private var _indexBuffer        : IndexBuffer3D ;
    private var _lineCount          : Int  ;
    
    
    
    public function SegmentSet()
    {
        
        super() ;
        
        if( _nullAnimation == null )
        {
            
            _nullAnimation = new NullAnimation() ;
            
        }
        
        _vertices       = new Vector() ;
        _segments       = new Vector() ;
        _numVertices    = 0 ;
        _indices        = new Vector() ;
        material        = new SegmentMaterial() ;
        
    }
    
    
    public function addSegment( segment : Segment )
    {
        
        segment.index           = _vertices.length ;
        segment.segmentsBase    = this ;
        _segments.push( segment ) ;
        
        updateSegment( segment ) ;
        
        var index               = _lineCount << 2 ;
        
        _indices.push( index, index + 1, index + 2, index + 3, index + 2, index + 1 ) ;
        
        _numVertices            = _vertices.length / 11 ;
        _numIndices             = _indices.length ;
        _vertexBufferDirty      = true ;
        _indexBufferDirty       = true ;
        _lineCount++ ;
        
    }
    
    
    public function updateSegment( segment : Segment )
    {
        
        //to do add support for curve segment
        var start               = segment._start ;
        var end                 = segment._end ;
        var startX              = start.x ;
        var startY              = start.y ;
        var startZ              = start.z ;
        var endX                = end.x ;
        var endY                = end.y ;
        var endZ                = end.z ;
        var startR              = segment._startR ;
        var startG              = segment._startG ;
        var startB              = segment._startB ;
        var endR                = segment._endR ;
        var endG                = segment._endG ;
        var endB                = segment._endB ;
        var index               = segment.index ;
        var t                   = segment.thickness ;
        
        _vertices[ index++ ]    = startX ;
        _vertices[ index++ ]    = startY ;
        _vertices[ index++ ]    = startZ ;
        _vertices[ index++ ]    = endX ;
        _vertices[ index++ ]    = endY ;
        _vertices[ index++ ]    = endZ ;
        _vertices[ index++ ]    = t ;
        _vertices[ index++ ]    = startR ;
        _vertices[ index++ ]    = startG ;
        _vertices[ index++ ]    = startB ;
        _vertices[ index++ ]    = 1 ;
        
        _vertices[ index++ ]    = endX ;
        _vertices[ index++ ]    = endY ;
        _vertices[ index++ ]    = endZ ;
        _vertices[ index++ ]    = startX ;
        _vertices[ index++ ]    = startY ;
        _vertices[ index++ ]    = startZ ;
        _vertices[ index++ ]    = -t ;
        _vertices[ index++ ]    = endR ;
        _vertices[ index++ ]    = endG ;
        _vertices[ index++ ]    = endB ;
        _vertices[ index++ ]    = 1 ;
        
        _vertices[ index++ ]    = startX ;
        _vertices[ index++ ]    = startY ;
        _vertices[ index++ ]    = startZ ;
        _vertices[ index++ ]    = endX ;
        _vertices[ index++ ]    = endY ;
        _vertices[ index++ ]    = endZ ;
        _vertices[ index++ ]    = -t ;
        _vertices[ index++ ]    = startR ;
        _vertices[ index++ ]    = startG ;
        _vertices[ index++ ]    = startB ;
        _vertices[ index++ ]    = 1 ;
        
        _vertices[ index++ ]    = endX ;
        _vertices[ index++ ]    = endY ;
        _vertices[ index++ ]    = endZ ;
        _vertices[ index++ ]    = startX ;
        _vertices[ index++ ]    = startY ;
        _vertices[ index++ ]    = startZ ;
        _vertices[ index++ ]    = t ;
        _vertices[ index++ ]    = endR ;
        _vertices[ index++ ]    = endG ;
        _vertices[ index++ ]    = endB ;
        _vertices[ index++ ]    = 1 ;
        
        _vertexBufferDirty      = true ;
        
    }
    
    
    private function remove( index : Int )
    {
        
        var indVert = _indices[ index ] * 11 ;
        _indices.splice( index, 6 ) ;
        _vertices.splice( indVert, 44 ) ;
        
        _numVertices        = _vertices.length / 11 ;
        _numIndices         = _indices.length ;
        _vertexBufferDirty  = true ;
        _indexBufferDirty   = true ;
        
    }
    
    
    public function removeSegment( segment : Segment )
    {
        //to do, add support curve indices/offset
        for ( i in 0..._segments.length )
        {
            
            if( _segments[ i ] == segment )
            {
                
                _segments.splice( i, 1 ) ;
                remove( segment.index ) ;
                _lineCount-- ;
                
            } 
            else
            {
                
                _segments[ i ].index    = index ;
                index                   += 6 ;
                
            }
            
        }
        
        _vertexBufferDirty  = true ;
        _indexBufferDirty   = true ;
        
    }
    
    
    private  function clearSegments()
    {
        
        _vertices.length    = 0 ;
        _indices.length     = 0 ;
        _segments.length    = 0 ;
        _vertexBufferDirty  = true ;
        _indexBufferDirty   = true ;
        
    }
    
    
    private function getVertexBuffer( context: Context3D, contextIndex: Int ) : VertexBuffer3D
    {
        
        if( _vertexBufferDirty ) 
        {
            
            _vertexBuffer       = context.createVertexBuffer( _numVertices, 11 ) ;
            _vertexBuffer.uploadFromVector( _vertices, 0, _numVertices ) ;
            _vertexBufferDirty  = false ;
            
        }
        
        return _vertexBuffer ;
        
    }
    
    
    private function getUVBuffer( context : Context3D, contextIndex : Int ) : VertexBuffer3D
    {
        
        return null ;
        
    }
    
    
    private function getVertexNormalBuffer( context : Context3D, contextIndex : Int ) : VertexBuffer3D
    {
        
        return null ;
        
    }
    
    
    private function getVertexTangentBuffer( context : Context3D, contextIndex : Int ) : VertexBuffer3D
    {
        
        return null ;
        
    }
    
    
    private function getIndexBuffer( context : Context3D, contextIndex : Int ) : IndexBuffer3D
    {
        
        if( _indexBufferDirty )
        {
            
            _indexBuffer        = context.createIndexBuffer( _numIndices ) ;
            _indexBuffer.uploadFromVector( _indices, 0, _numIndices ) ;
            _indexBufferDirty   = false ;
            
        }
        
        return _indexBuffer ;
        
    }
    
    
    public var mouseDetails( get_mouseDetails, null ) : Bool ;
    
    
    private function get_mouseDetails() : Bool
    {
        
        return false ;
        
    }
    
    
    public var numTriangles( get_numTriangles, null ) : Int ;
    
    
    private function get_numTriangles() : Int
    {
        
        return Std.parseInt( _numIndices / 3 ) ;
        
    }
    
    
    public var sourceEntity( get_sourceEntity, null ) : Entity ;
    
    
    private function get_sourceEntity() : Entity
    {
        
        return this ;
        
    }
    
    
    public var castsShadows( get_castsShadows, null ) : Bool ;
    
    
    private function get_castsShadows() : Bool
    {
        
        return false ;
        
    }
    
    
    public var material( get_material, set_material ) : MaterialBase ;
    
    
    private function get_material() : MaterialBase
    {
        return _material ;
    }
    
    
    private function set_material( value : MaterialBase )
    {
        
        if( value == _material ) return value ;
        if( _material ) _material.removeOwner( this ) ;
        _material = value ;
        if( _material ) _material.addOwner( this ) ;
        return _material ;
        
    }
    
    
    public var animation( get_animation, set_animation ) : AnimaitionBase ;
    
    
    private function get_animation() : AnimationBase
    {
        return _nullAnimation ;
    }
    
    
    public var animationState( get_animation, null ) : AnimationStateBase ;
    
    
    private function get_animationState() : AnimationStateBase
    {
        
        return _animationState ;
        
    }
    
    
    override private  function getDefaultBoundingVolume() : BoundingVolumeBase
    {
        
        return new BoundingSphere() ;
        
    }
    
    
    override private  function updateBounds()
    {
        
        // todo: fix bounds
        _bounds.fromExtremes( -100, -100, 0, 100, 100, 0 ) ;
        _boundsInvalid = false ;
        
    }
    
    
    override private  function createEntityPartitionNode() : EntityNode
    {
        
        return new RenderableNode( this ) ;
        
    }
    
    
}
